﻿using Sunny.UI;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Forms;
using TorchSharp;
using YoloSharp;
using static TorchSharp.torch.nn;


namespace Form_YOLO_NET8
{
    public partial class MainForm : Form
    {

        // private readonly string targetPath = Path.Combine(Application.StartupPath, "UploadedFiles");
        string exePath = @"labelme.exe";  // 定义labelme文件的路径
        public static string ModelName = "";  //当前模型名字
        public static string FileDir = "";    //当前总文件夹路径

        public MainForm()
        {
            InitializeComponent();
        }


        private void btn_CreateFiles_Click(object sender, EventArgs e)
        {
            try
            {
                if (tb_FileName.Text.Length > 0)
                {
                    ModelName = $"{DateTime.Now.ToString("yyyyMMddHHmm")}" + "_" + tb_FileName.Text.ToString();
                    tb_FileName.Text = ModelName;

                    string basePath = @"..\..\..\Assets\DataSets";

                    // 创建主文件夹
                    string mainFolder = Path.Combine(basePath, ModelName);
                    Directory.CreateDirectory(mainFolder);
                    FileDir = mainFolder;

                    // 在主文件夹中创建两个子文件夹
                    string subFolder1 = Path.Combine(mainFolder, "images");
                    string subFolder2 = Path.Combine(mainFolder, "labels");
                    string subFolder3 = Path.Combine(mainFolder, "labelmeLabels");
                    string subFolder4 = Path.Combine(mainFolder, "output");


                    Directory.CreateDirectory(subFolder1);
                    Directory.CreateDirectory(subFolder2);
                    Directory.CreateDirectory(subFolder3);
                    Directory.CreateDirectory(subFolder4);




                    // 验证创建结果
                    MessageBox.Show($"文件夹结构创建成功！\n文件路径：{FileDir} \n 子文件夹1：{subFolder1}\n 子文件夹2：{subFolder2}\n", "信息",
                    MessageBoxButtons.OK, MessageBoxIcon.Information);
                    tb_TrainFileDir.Text = FileDir;



                    LogMessage.WriteLog($"文件夹结构创建成功！\n文件路径：{FileDir} \n 子文件夹1：{subFolder1}\n 子文件夹2：{subFolder2}\n", LogMessage.LogLevel.Info);

                }
                else
                {
                    MessageBox.Show("请输入模型名称", "警告",
                    MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
            }
            catch (Exception ex)
            {
                LogMessage.WriteLog($"创建文件夹报错，{ex.Message}", LogMessage.LogLevel.Warning);
                MessageBox.Show($"[创建文件夹报错]\n {ex.Message}", "警告", MessageBoxButtons.OK, MessageBoxIcon.Information);

            }

        }

        private void SelectFile_Click(object sender, EventArgs e)
        {
            try
            {
                using (var folderDialog = new FolderBrowserDialog())
                {
                    // 配置对话框参数
                    folderDialog.Description = "请选择文件夹";
                    folderDialog.ShowNewFolderButton = true;
                    folderDialog.RootFolder = Environment.SpecialFolder.MyComputer;

                    // 显示对话框并处理结果
                    if (folderDialog.ShowDialog() == DialogResult.OK)
                    {
                        FileDir = folderDialog.SelectedPath;
                        tb_FileName.Text = Path.GetFileName(FileDir);

                        tb_TrainFileDir.Text = FileDir;

                    }
                }
            }
            catch (Exception ex)
            {
                LogMessage.WriteLog($"选择文件夹报错，{ex.Message}", LogMessage.LogLevel.Warning);
                MessageBox.Show($"[选择文件夹报错]\n {ex.Message}", "警告", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }

        }


        private void FileAddPanel_DragEnter(object sender, DragEventArgs e)
        {
            // 检查拖入的是否为文件
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
                e.Effect = DragDropEffects.Copy;
            else
                e.Effect = DragDropEffects.None;
        }

        private void FileAddPanel_DragDrop(object sender, DragEventArgs e)
        {
            try
            {
                string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);

                // 创建目标目录（如果不存在）
                string targetPath = Path.Combine(FileDir, "images");
                Directory.CreateDirectory(targetPath);

                foreach (var file in files)
                {
                    if (File.Exists(file))
                    {
                        string fileName = Path.GetFileName(file);
                        string destPath = Path.Combine(targetPath, fileName);

                        // 移动文件（覆盖已存在文件）
                        File.Move(file, destPath, true);

                        // 记录成功日志
                        Debug.Print($"[成功] {fileName}");
                    }
                }

                FileAddPanel.Text = $"图片导入完成，共导入{files.Length}张图片";
                CkB_ImageUpload.Checked = true;

                LogMessage.WriteLog($"图片导入完成，共导入{files.Length}张图片", LogMessage.LogLevel.Info);

            }
            catch (Exception ex)
            {
                LogMessage.WriteLog($"图片导入报错，{ex.Message}", LogMessage.LogLevel.Warning);
                MessageBox.Show($"[图片导入报错]\n {ex.Message}", "警告", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
        }

        private void btn_OpenLabelme_Click(object sender, EventArgs e)
        {

            try
            {

                Label_Message.Text = $"1.打开目录：{Path.Combine(FileDir, "images")} \n " +
                    $"输出路径：{Path.Combine(FileDir, "labelmeLabels")} \n";


                Process process = new Process();
                process.StartInfo.FileName = exePath;
                // 启动程序
                process.Start();
                Debug.Print("程序已启动。");
            }
            catch (Exception ex)
            {

                LogMessage.WriteLog($"启动labelme报错，{ex.Message}", LogMessage.LogLevel.Warning);
                MessageBox.Show($"[无法打开文件]\n {ex.Message}", "警告", MessageBoxButtons.OK, MessageBoxIcon.Information);

            }
        }

        private void btn_ImageUpload_Click(object sender, EventArgs e)
        {
            if (FileDir.Length > 5)
            {
                FileAddPanel.Visible = true;
            }
            else
            {
                MessageBox.Show("请选择模型地址", "警告",
                    MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
        }

        private void btn_ChangeLabels_Click(object sender, EventArgs e)
        {
            try
            {
                LabelToYolo _LabelToYolo = new LabelToYolo();
                List<string> ModelClass = _LabelToYolo.GetClass(Path.Combine(FileDir, "labelmeLabels"));
                _LabelToYolo.Labelme2YoloSeg(ModelClass, Path.Combine(FileDir, "labelmeLabels"), Path.Combine(FileDir, "labels"));
                MessageBox.Show($"[标签格式转换成功]\n标签内容：{ModelClass}", "信息", MessageBoxButtons.OK, MessageBoxIcon.Information);

            }
            catch (Exception ex)
            {
                LogMessage.WriteLog($"标签格式转换报错，{ex.Message}", LogMessage.LogLevel.Warning);
                MessageBox.Show($"[标签格式转换报错]\n {ex.Message}", "警告", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }



        }

        private void OpenFile_Click(object sender, EventArgs e)
        {
            try
            {
                ProcessStartInfo startInfo = new ProcessStartInfo()
                {

                    FileName = FileDir,
                    UseShellExecute = true,
                    Verb = "open"
                };
                Process.Start(startInfo);

            }
            catch (Exception ex)
            {
                LogMessage.WriteLog($"文件夹打卡失败，{ex.Message}", LogMessage.LogLevel.Warning);
                MessageBox.Show($"[文件夹打卡失败]\n {ex.Message}", "警告", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
        }

        private void TrackBar_epochs_ValueChanged(object sender, EventArgs e)
        {
            lb_epochs.Text = $"模型训练{TrackBar_epochs.Value.ToString()}轮";
            progressBar1.Maximum = TrackBar_epochs.Value;
        }

        private void MainForm_Load(object sender, EventArgs e)
        {
            foreach (YoloType type in Enum.GetValues(typeof(YoloType)))
            {
                cb_YoloType.Items.Add(type.ToString());
            }

            cb_YoloType.SelectedItem = YoloType.Yolov11.ToString();

            ck_UseGPU.Checked = true;
        }

        private void bt_SelectTrainFileDir_Click(object sender, EventArgs e)
        {
            try
            {
                using (var folderDialog = new FolderBrowserDialog())
                {
                    // 配置对话框参数
                    folderDialog.Description = "请选择文件夹";
                    folderDialog.ShowNewFolderButton = true;
                    folderDialog.RootFolder = Environment.SpecialFolder.MyComputer;

                    // 显示对话框并处理结果
                    if (folderDialog.ShowDialog() == DialogResult.OK)
                    {
                        FileDir = folderDialog.SelectedPath;
                        tb_FileName.Text = Path.GetFileName(FileDir);

                        tb_TrainFileDir.Text = FileDir;

                    }
                }
            }
            catch (Exception ex)
            {
                LogMessage.WriteLog($"选择文件夹报错，{ex.Message}", LogMessage.LogLevel.Warning);
                MessageBox.Show($"[选择文件夹报错]\n {ex.Message}", "警告", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
        }
        Segmenter segmenter = null;
        Thread backgroundThread_TrainModel;
        //按钮开始模型训练
        private void btn_StartTrain_Click(object sender, EventArgs e)
        {
            try
            {
                if (FileDir != "")
                {
                    string[] expectedSubFolders = { "images", "labels" };  // 预期的子文件夹名称
                    string?[] actualSubFolders = Directory.GetDirectories(FileDir).Select(Path.GetFileName).ToArray(); // 获取实际存在的子文件夹名称
                    bool isComplete = expectedSubFolders.All(expected => actualSubFolders.Contains(expected)); // 检查是否所有预期的子文件夹都存在
                    if (isComplete)
                    {
                        string trainDataPath = $"{FileDir}"; // Training data path, it should be the same as coco dataset.
                        string valDataPath = $"{FileDir}"; // If valDataPath is "", it will use trainDataPath as validation data.
                        string outputPath = $"{FileDir}\\output";
                        string preTraindModelPath = @"..\..\..\Assets\PreTrainedModels\yolov11n-seg.bin"; // Pretrained model path.


                        int batchSize = (int)txt_batchSize.Value;
                        int sortCount = 80;
                        int epochs = TrackBar_epochs.Value;

                        //float predictThreshold = 0.9f;
                        //float iouThreshold = 0.45f;

                        YoloType yoloType = YoloType.Yolov11;
                        DeviceType deviceType = ck_UseGPU.Checked ? DeviceType.CUDA : DeviceType.CPU;
                        ScalarType dtype = ScalarType.Float32;
                        YoloSize yoloSize = YoloSize.n;


                        TrainView_MessageDisplay($"文件夹内容完整,准备开始训练...........\n");

                        // 创建后台线程
                        backgroundThread_TrainModel = new Thread(() =>
                       {
                           Stopwatch stopwatch = new Stopwatch();
                           stopwatch.Start();
                           segmenter = new Segmenter(sortCount, yoloType: yoloType, deviceType: deviceType, yoloSize: yoloSize, dtype: dtype);
                           segmenter.LoadModel(preTraindModelPath, skipNcNotEqualLayers: true);

                           segmenter.stopTrain = false;
                           segmenter.Train_Form(TrainView_MessageDisplay, trainDataPath, valDataPath, outputPath: outputPath, batchSize: batchSize, epochs: epochs, useMosaic: false);
                           stopwatch.Stop();
                           // 输出或记录运行时间
                           TrainView_MessageDisplay($"训练完成，总用时{stopwatch.ElapsedMilliseconds}ms\n");

                           MessageBox.Show($"YOLO模型训练完成", "信息", MessageBoxButtons.OK, MessageBoxIcon.Information);
                           //  Console.WriteLine("运行时间: {0} 毫秒", stopwatch.ElapsedMilliseconds);
                       });
                        backgroundThread_TrainModel.IsBackground = true;
                        backgroundThread_TrainModel.Start();




                    }
                    else
                    {
                        MessageBox.Show($"[文件夹内容不完整]\n 请重新选择文件夹", "警告", MessageBoxButtons.OK, MessageBoxIcon.Information);
                        return;
                        // TrainView_MessageDisplay("文件夹内容不完整");
                        //foreach (var missingFolder in expectedSubFolders.Except(actualSubFolders))
                        //{
                        //    Console.WriteLine(missingFolder);
                        //}
                    }

                }
                else
                {

                    MessageBox.Show($"[文件夹未选中]\n 请重新选择文件夹", "警告", MessageBoxButtons.OK, MessageBoxIcon.Information);
                }

            }
            catch (Exception ex)
            {
                LogMessage.WriteLog($"Yolo模型开始训练报错，{ex.Message}", LogMessage.LogLevel.Warning);
                MessageBox.Show($"[Yolo模型开始训练报错]\n {ex.Message}", "警告", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }

        }

        private void btn_StopTrain_Click(object sender, EventArgs e)
        {
            if (segmenter != null)
            {
                segmenter.stopTrain = true;
            }


        }

        public void TrainView_MessageDisplay(string msg)
        {

            UpdateUIThreadSafe(() =>
            {
                // 添加新的报警信息
                TrainModelDisplay.SelectionColor = Color.Black;
                TrainModelDisplay.AppendText(msg + Environment.NewLine);

                // 自动滚动到底部
                TrainModelDisplay.SelectionStart = TrainModelDisplay.Text.Length;
                TrainModelDisplay.ScrollToCaret();


                // 定义正则表达式模式来匹配 epoch 和 valLoss
                string pattern = @"Epoch:\s*(\d+),\s*Val Loss:\s*([0-9.]+)";
                Regex regex = new Regex(pattern); // 创建正则表达式对象
                Match match = regex.Match(msg); // 尝试匹配输入字符串
                if (match.Success)
                {
                    string epochValue = match.Groups[1].Value;
                    string valLossValue = match.Groups[2].Value;

                    // 将提取的字符串转换为整数和浮点数
                    if (int.TryParse(epochValue, out int epoch) && float.TryParse(valLossValue, out float valLoss))
                    {
                        progressBar1.Value = epoch + 1;
                        //Console.WriteLine($"Epoch: {epoch}");
                        //Console.WriteLine($"Val Loss: {valLoss}");
                    }
                }


            });

        }
        private delegate void UpdateUIDelegate(Action action);
        private void UpdateUIThreadSafe(Action action)
        {
            if (this.InvokeRequired)
            {
                // 如果需要，封送操作到UI线程
                this.Invoke(new UpdateUIDelegate(UpdateUIThreadSafe), new object[] { action });
            }
            else
            {
                // 直接在UI线程上执行操作
                action();
            }
        }

        private void btn_TestModelDir_Click(object sender, EventArgs e)
        {
            using (OpenFileDialog ofd = new OpenFileDialog())
            {
                ofd.Title = "选择文件";
                ofd.Filter = "BIN文件|*.bin";
                ofd.Multiselect = true;

                if (ofd.ShowDialog() == DialogResult.OK)
                {
                    tb_TestModelDir.Text = $"{string.Join(", ", ofd.FileNames)}";
                }
            }
        }
        Segmenter test_segmenter = null;
        string tset_ImgDir = null;
        Bitmap testBitmap;
        private void btn_LoadModel_Click(object sender, EventArgs e)
        {
            try
            {
                if (tb_TestModelDir.Text.Length > 10)
                {
                    int sortCount = 80;
                    YoloType yoloType = YoloType.Yolov11;
                    DeviceType deviceType = DeviceType.CUDA;
                    ScalarType dtype = ScalarType.Float32;
                    YoloSize yoloSize = YoloSize.n;

                    segmenter = new Segmenter(sortCount, yoloType: yoloType, deviceType: deviceType, yoloSize: yoloSize, dtype: dtype);
                    segmenter.LoadModel($"{tb_TestModelDir.Text}");
                    label_RunTime.Text = "模型加载成功";
                    //Debug.Print("模型加载成功");
                }

            }
            catch (Exception ex) { label_RunTime.Text = "模型加载失败"; Debug.Print($"模型加载失败，{ex.Message}"); }
        }

        bool IsDetectPicture = true;

        private void btn_CheckImg_Click(object sender, EventArgs e)
        {
            using (OpenFileDialog ofd = new OpenFileDialog())
            {
                ofd.Title = "选择文件";
                ofd.Filter = "JPG|*.jpg*";
                ofd.Multiselect = true;

                if (ofd.ShowDialog() == DialogResult.OK)
                {
                    tset_ImgDir = ofd.FileName;
                    Debug.Print($"已经打开文件地址：{ofd.FileName}");

                    testBitmap = new Bitmap(tset_ImgDir);
                    this.imageShow1.InPutBuffer(testBitmap);


                    //pb_testDisplay.Image = testBitmap;
                    //pb_testDisplay.SizeMode = PictureBoxSizeMode.Zoom;
                    IsDetectPicture = true;
                }
            }



        }

        int ImgListnumber = 0;
        private void btn_ImageRecognition_Click(object sender, EventArgs e)
        {
            Bitmap bitmapImg = null;
            if (IsDetectPicture)
            {
                if (testBitmap != null)
                {
                    bitmapImg = testBitmap;
                }
                else { Debug.Print("请选择识别图片"); return; }
            }
            else
            {
                if (TestImgList.Count > 0)
                {
                    if (ImgListnumber > TestImgList.Count - 1) { ImgListnumber = 0; }
                    bitmapImg = new Bitmap(TestImgList[ImgListnumber]);
                    ImgListnumber++;
                }
                else { Debug.Print("请选择识别的图片文件夹"); return; }

            }
            float predictThreshold = (float)tb_predictThreshold.Value;
            float iouThreshold = (float)tb_iouThreshold.Value;


            Stopwatch modrunTime = new Stopwatch();
            modrunTime.Start();
            var (predictResult, bitmap) = segmenter.ImagePredict(bitmapImg, predictThreshold, iouThreshold);
            modrunTime.Stop();
            bitmapImg.Dispose();
            label_RunTime.Text = $"本次运行时间：{modrunTime.ElapsedMilliseconds}ms";
            // Console.WriteLine("第一次运行时间: {0} 毫秒", );

            if (predictResult.Count > 0)
            {
                // Draw results
                Graphics g = Graphics.FromImage(bitmap);
                foreach (var result in predictResult)
                {
                    Point point = new Point(result.X, result.Y);
                    string str = string.Format($"Sort:{result.ClassID}, Score:{result.Score * 100:F1}%");
                    g.DrawRectangle(Pens.Red, new Rectangle(point, new Size(result.W, result.H)));
                    g.DrawString(str, new Font(FontFamily.GenericMonospace, 10), new SolidBrush(Color.Red), point);
                    Console.WriteLine(str);
                }
                g.Save();

                this.imageShow1.InPutBuffer(bitmap);
                //pb_testDisplay.Image = bitmap;
                //pb_testDisplay.SizeMode = PictureBoxSizeMode.Zoom;
                Debug.Print($"识别成功，共{predictResult.Count}项");

            }
            else
            {
                Debug.Print("无识别到内容");
            }

            GC.Collect();

        }

        List<string> TestImgList = new List<string>();
        private void btn_CheckImgFile_Click(object sender, EventArgs e)
        {
            // 使用 FolderBrowserDialog 选择文件夹
            using (FolderBrowserDialog fbd = new FolderBrowserDialog())
            {
                fbd.Description = "选择文件夹";
                if (fbd.ShowDialog() == DialogResult.OK)
                {

                    string folderPath = fbd.SelectedPath;
                    Debug.Print($"已经打开文件夹地址：{folderPath}");

                    string[] files = Directory.GetFiles(folderPath, "*.jpg");
                    TestImgList.Clear();
                    foreach (string file in files)
                    {
                        TestImgList.Add(file);
                        //  Debug.Print($"文件夹中的 JPG 文件：{file}");
                        // 你可以在这里添加更多处理逻辑，比如显示文件信息或加载文件到某个控件中
                    }
                    label_RunTime.Text = $"共找到{TestImgList.Count}张图片";
                    IsDetectPicture = false;
                }
            }
        }

        private void btn_DistanceConversion_Click(object sender, EventArgs e)
        {
            Form_DistanceMeasurementcs _Form = new Form_DistanceMeasurementcs();
            _Form.StartPosition = FormStartPosition.CenterScreen;

         
            _Form.ShowDialog();
            _Form.Dispose();
        }
    }
}

/*
 亲爱的宣伊：
    电子版本的书信在电脑上打了好几遍，但是还是迟迟的不知道怎么动笔，脑袋里面没有多少的墨水，不知道怎么表达比较好，
现在想来想去还是采用比较直接的直叙的手法进行阐述。

    在这个很快的世界里面，多少次在那边思考真的很幸运能遇见了宝宝，然后就是那种被幸福所充满的那种感觉的。
我喜欢每天早上醒来，第一时间看到你的消息，那仿佛是我一天中最美好的开始。
我喜欢宝宝和我分享身边的大事小情，无论是琐碎的日常还是重要的决定，我都愿意倾听，都愿意与你一同分担或庆祝。
喜欢宝宝把生活中的点点滴滴都告诉我，让我感受到了被需要的幸福的。


 
    可惜我文笔平平，道不尽对你的绵绵爱意，但我不想错过每个与你相关的念头。所以我在深夜持笔，把细水长流的爱混着夜色悄无声息的写给你。好吧，这句话是不是感觉超级有水平
这个是我找了好久好久找到的，当时感觉整个太平淡了，然后就找了个感觉高端一点点的结尾，嘻嘻嘻。
 
 */